A wellbore trajectory is a roadmap for drilling wells in the oil and gas industry. It shows the path from the surface to the underground reservoir, so that drillers know where to go. This information is crucial for efficient and safe drilling. It ensures that drillers reach the right spot to extract oil and gas resources.
Four XML data files were used to create an interactive wellbore trajectory plot for Well 15/9_F-5. The data was retrieved from the Volve dataset in the Wellsite Information Transfer Standard Markup Language (WITSML) folder. XML (Extensible Markup Language) is a digital file format that makes it easy to share well information between different computer systems and companies.
We import a few Python libraries that have specific functions to make data processing and visualization smooth sailing in this code process:
By importing these libraries, we can efficiently handle data and create interactive visualizations as we move forward in the code.
# Import python libraries
import numpy as np
from bs4 import BeautifulSoup
import pandas as pd
import plotly.graph_objects as go
from plotly.offline import plot
The following code is in charge of retrieving the well trajectory data from XML files for Well 15/9-F-5. The trajectory data is distributed across four XML files, specifically named '1.xml', '2.xml', '3.xml', and '4.xml'. The variable Trajectory_files holds the file names, while the legend_names variable captures the descriptive labels for different sections of the well trajectory. These legend names offer valuable context about each section of the well trajectory, enhancing the understanding of the data.
# Read the Well 15/9-F-5 Trajectory XML files
Trajectory_files = ['1.xml', '2.xml', '3.xml', '4.xml']
legend_names = ['17.5 in. Section (T-574976-1)', '12.25 in. Section (T-704051-1)', '8.5 in. Section (T-710489-1)',
'36 in. Section (T-555809-1)'] # Taken from the MetaDataFileInfo.txt file included in the folder
Now, let's move on to the next step. The code below manages in handling the task of reading XML files that contain data related to the trajectory of a well. It makes use of the BeautifulSoup library, which provides convenient tools for processing and extracting information from XML files.
To begin, an empty list named data_xml is created to hold the parsed XML data. The code then iterates through each file name in the Trajectory_files list. For each file, it opens the XML file, reads its contents, and assigns them to the variable data.
Using the BeautifulSoup library, the code creates a BeautifulSoup object called data_xml by passing in the data and specifying the parser type as XML. This object represents the parsed XML data from the file. The data_xml object is then added to the data_xml list.
The data_xml list now contains a collection of BeautifulSoup objects, with each object representing the parsed XML data from a specific file. These objects can be further processed and analyzed to extract the desired information from the XML files containing the well trajectory data.
# Reading XML Files into BeautifulSoup Objects
data_xml = []
for file in Trajectory_files:
with open(file) as f:
data = f.read()
data_xml.append(BeautifulSoup(data, 'xml'))
Let's proceed to the next step. The following code is used to extract and display the data columns from the XML files that hold information about the well trajectory. It iterates over the data_xml list, which contains the parsed XML data, and for each file, it retrieves and prints the data columns. The extraction process involves finding all the tags in the XML data, and the resulting columns are then displayed as output. By executing this code, we see the complete list of columns for all four XML files.
# Printing the data columns of the XML files
for i, data in enumerate(data_xml):
print(f"Data columns of {Trajectory_files[i]}:")
columns = set([tag.name for tag in data.find_all()])
print("\n".join(columns))
print()
Data columns of 1.xml: dTimTrajStart azi sagIncCor gravAxialRaw sourceName gravTran1Raw nameWell md gravTotalFieldCalc rawData trajectoryStation magTotalFieldCalc aziVertSect commonData magTran1DrlstrCor magTotalFieldReference magDrlstrCorUsed stnMagDeclUsed dTimStn dispNs gravTotalFieldReference gravTran1AccelCor mdDelta statusTrajStation magDipAngleCalc tvd nameWellbore itemState priv_ipOwner gravAxialAccelCor corUsed vertSect stnGridCorUsed sagCorUsed rateBuild incl gravTotalUncert magTran1Raw sagAziCor trajectory magAxialRaw gtf gridCorUsed magTran2DrlstrCor magXAxialCorUsed dispEw mdMx trajectorys gravTran2Raw priv_dTimReceived rateTurn mdMn magTran2Raw valid magAxialDrlstrCor memory typeTrajStation dls dTimLastChange priv_userOwner dTimCreation name dTimTrajEnd dirSensorOffset magDeclUsed magTotalUncert tvdDelta serviceCompany gravTran2AccelCor dipAngleUncert aziRef mtf gravAccelCorUsed magDipAngleReference Data columns of 2.xml: dTimTrajStart azi sagIncCor gravAxialRaw sourceName gravTran1Raw nameWell md gravTotalFieldCalc rawData trajectoryStation magTotalFieldCalc aziVertSect commonData magTran1DrlstrCor magTotalFieldReference magDrlstrCorUsed stnMagDeclUsed dTimStn dispNs gravTotalFieldReference gravTran1AccelCor mdDelta statusTrajStation magDipAngleCalc tvd nameWellbore itemState priv_ipOwner gravAxialAccelCor corUsed vertSect stnGridCorUsed sagCorUsed rateBuild incl gravTotalUncert magTran1Raw sagAziCor trajectory magAxialRaw gtf gridCorUsed magTran2DrlstrCor magXAxialCorUsed dispEw mdMx trajectorys gravTran2Raw priv_dTimReceived rateTurn mdMn magTran2Raw valid magAxialDrlstrCor memory typeTrajStation dls dTimLastChange priv_userOwner dTimCreation name dTimTrajEnd dirSensorOffset magDeclUsed magTotalUncert tvdDelta serviceCompany gravTran2AccelCor dipAngleUncert aziRef mtf gravAccelCorUsed magDipAngleReference Data columns of 3.xml: dTimTrajStart azi sagIncCor gravAxialRaw sourceName gravTran1Raw nameWell md gravTotalFieldCalc rawData trajectoryStation magTotalFieldCalc aziVertSect commonData magTran1DrlstrCor magTotalFieldReference magDrlstrCorUsed stnMagDeclUsed dTimStn dispNs gravTotalFieldReference gravTran1AccelCor mdDelta statusTrajStation magDipAngleCalc tvd nameWellbore itemState priv_ipOwner gravAxialAccelCor corUsed vertSect stnGridCorUsed sagCorUsed rateBuild incl gravTotalUncert magTran1Raw sagAziCor trajectory magAxialRaw gtf gridCorUsed magTran2DrlstrCor magXAxialCorUsed dispEw mdMx trajectorys gravTran2Raw priv_dTimReceived rateTurn mdMn magTran2Raw valid magAxialDrlstrCor memory typeTrajStation dls dTimLastChange priv_userOwner dTimCreation name dTimTrajEnd dirSensorOffset magDeclUsed magTotalUncert tvdDelta serviceCompany gravTran2AccelCor dipAngleUncert aziRef mtf gravAccelCorUsed magDipAngleReference Data columns of 4.xml: dTimTrajStart azi sagIncCor gravAxialRaw sourceName gravTran1Raw nameWell md gravTotalFieldCalc rawData trajectoryStation magTotalFieldCalc aziVertSect commonData magTran1DrlstrCor magTotalFieldReference magDrlstrCorUsed stnMagDeclUsed dTimStn dispNs gravTotalFieldReference gravTran1AccelCor mdDelta statusTrajStation magDipAngleCalc tvd nameWellbore itemState priv_ipOwner gravAxialAccelCor corUsed vertSect stnGridCorUsed sagCorUsed rateBuild incl gravTotalUncert magTran1Raw sagAziCor trajectory magAxialRaw gtf gridCorUsed magTran2DrlstrCor magXAxialCorUsed dispEw mdMx trajectorys gravTran2Raw priv_dTimReceived rateTurn mdMn magTran2Raw valid magAxialDrlstrCor memory typeTrajStation dls dTimLastChange priv_userOwner dTimCreation name dTimTrajEnd dirSensorOffset magDeclUsed magTotalUncert tvdDelta serviceCompany gravTran2AccelCor dipAngleUncert aziRef mtf gravAccelCorUsed magDipAngleReference
Based on the result above, we have obtained a comprehensive list of column names present in the XML files. Now, we select the specific column names to be utilized in plotting and visualizing the data.
Following the code below is responsible for extracting the data columns from the XML files. It initializes an empty list called 'dfs' to store the extracted data frames. By iterating over the 'data_xml' objects, it creates a new data frame for each XML file. The specified columns, namely 'azi', 'md', 'tvd', 'incl', 'dispNs', and 'dispEw', are extracted from the XML data and stored in their respective columns within the data frame. So, each data frame is appended to the 'dfs' list for further processing.
# Extracting Data Columns from XML Files
columns = ['azi', 'md', 'tvd', 'incl', 'dispNs', 'dispEw']
dfs = []
for i, data in enumerate(data_xml):
df = pd.DataFrame()
for col in columns:
df[col] = [float(x.text) for x in data.find_all(col)]
dfs.append(df)
Moving forward, the code below handles for generating traces that is used to create plots for each dataset. It starts by initializing an empty list called 'traces' to store the generated traces. The 'colors' list defines the colors to be assigned to each dataset, and the line width is set to 10.
Using a loop combined with the 'enumerate' function, the code iterates over the 'dfs' list, which contains the data frames. For each data frame, a 3D scatter trace is created using the 'go.Scatter3d' function from the Plotly library. The trace is defined by specifying the x, y, and z coordinates, setting the mode to lines, and configuring the line color and width based on the corresponding dataset index. The name of the trace is assigned using the 'legend_names' list. Each trace is added to the 'traces' list for further usage in the plotting process.
# Create traces for each dataset
traces = []
colors = ['red', 'blue', 'green', 'orange']
line_width = 10 # Adjust the line width here
for i, df in enumerate(dfs):
trace = go.Scatter3d(x=df['dispNs'], y=df['dispEw'], z=df['tvd'] * -1, mode='lines',
line=dict(color=colors[i], width=line_width), name=legend_names[i])
traces.append(trace)
Now comes the exciting part of this coding process. The following code is in charge of generating a visually appealing 3D plot of the well trajectory. It starts by creating a plot object using the 'go.Figure' function, and the 'traces' list, which holds the data to be plotted, is passed as input.
After that, the code proceeds to configure various plot settings. It sets the title, axis labels, and font sizes to ensure clear visualization. The aspect ratio is adjusted to maintain the proper proportions in the plot. Margins are specified to control the spacing around the plot. The legend is customized with a suitable title and font sizes.
Lastly, the plot is displayed for immediate viewing, and it is also saved as an HTML file for future reference or sharing. This allows us to conveniently explore and analyze the well trajectory in an interactive and visually appealing manner. The html file of the interactive well trajectory plot is available on the github repository for viewing.
# Create the figure
fig = go.Figure(data=traces)
# Set axis labels, title and the interactive trajectory plot settings
fig.update_layout(
title=dict(text="WELL 15/9-F-5 TRAJECTORY<br>Field Name: Volve Field", x=0.4, # Set the x position to center the title
xanchor='center'), # Center the title horizontally
hovermode='closest',
scene=dict(xaxis=dict(title='N-S DISPLACEMENT', titlefont=dict(size=20)),
yaxis=dict(title='E-W DISPLACEMENT', titlefont=dict(size=20)),
zaxis=dict(title='TRUE VERTICAL DEPTH', titlefont=dict(size=20))),
scene_aspectmode='manual',
scene_aspectratio=dict(x=0.7, y=0.7, z=1.5),
margin=dict(l=20, r=20, b=20, t=65),
font=dict(size=15),
legend=dict(title='ACTUAL TRAJECTORIES<br>Real Time SLB & Geoservice data', title_font=dict(size=18), font=dict(size=14)))
# Show the plot
fig.show()
# Save the plot as an HTML file
plot(fig, filename='Well Trajectory.html')
'Well Trajectory.html'
Equinor ASA (formerly Statoil) and the former Volve license partners, ExxonMobil Exploration & Production Norway AS and Bayerngas Norge AS, are credited for providing the VOLVE dataset under CC BY 4.0 license.
Thank you so much for visiting this GitHub repository and taking the time to read this notebook. I hope you found it informative and learned something new. Your support and interest are sincerely appreciated.